###############################################################################################
# Copyright (C) 2025 Griefed
#
# This script is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
# USA
#
# The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
###############################################################################################

<#
    .SYNOPSIS

    Start script to easily run your server pack. In order to run this script even easier, run the start.bat-file
    which was also shipped with this server pack.

    .DESCRIPTION

    A start-script supporting Forge, NeoForge, Fabric, Quilt and LegacyFabric as well as their supported Minecraft
    versions.

    This script downloads and installs the Modloader server depending on the settings in the acompanying variables.txt
    which was also shipped with this server pack. Should no suitable Java installation be found and your $JAVA-variable
    be set to "java", then a suitable Java-installation will also be downloaded and provided to this server pack.

    You can let the server restart by setting RESTART to true in your variables.txt. More information about the
    various settings in said file. Go check it out.

    .NOTES

    Start script generated by ServerPackCreator SPC_SERVERPACKCREATOR_VERSION_SPC.
    The template which was used in the generation of this script can be found at:
    https://github.com/Griefed/ServerPackCreator/blob/SPC_SERVERPACKCREATOR_VERSION_SPC/serverpackcreator-api/src/main/resources/de/griefed/resources/server_files/default_template.ps1

    By default, running Powershell scripts from untrusted sources is probably disabled on your system.
    As such, you will not be able to run the start.ps-scripts just yet. You need to allow running
    unsigned scripts first. See https://superuser.com/a/106363 for a short explanation on how to
    enable/allow running unsigned scripts with Powershell.
      You may run `start-process PowerShell -verb runas "Set-ExecutionPolicy RemoteSigned"` from a regular
      PowerShell to allow running of the start-script.
      ATTENTION:
          Bear in mind that this introduces a security risk on your system. After making the changes from the
          link above, you can run any Powershell script you like, and as such, introduce any and all security
          risk into your system. So, beware when running scripts from unknown sources.

    Powershell scripts by default can not be opened with a double-click if the path to said script
    contains spaces. If you wish to remedy this or want to read more about this behaviour, this article
    talks about it in great detail: https://blog.danskingdom.com/fix-problem-where-windows-powershell-cannot-run-script-whose-path-contains-spaces/
    You can thank Mircosoft for this. There is nothing the developers of ServerPackCreator can do about that.
    What you should do instead is:
      In your explorer, browse into the directory which contains your server pack.
      Shift-Rightclick into an empty space inside the directory
      Click "Open PowerShell window here"
      Type ".\start.ps1", without the "", and hit enter

      ATTENTION:
          Keep in mind though that things may still break when working with paths with spaces in them. If
          things still break with a path with spaces, even after trying the fixes from the link above, then I
          suggest moving things to a folder whose path contains no spaces.

    Depending on which modloader is set, different checks are run to ensure the server will start accordingly.
    If the modloader checks and setup are passed, Minecraft and EULA checks are run.
    If everything is in order, the server is started.

    Depending on the Minecraft version you will require a different Java version to run the server.
      1.16.5 and older requires Java 8 (Java 11 will run better and work with 99% of mods, give it a try)
        You may acquire a Java 8 install here: https://adoptium.net/temurin/releases/?variant=openjdk8&version=8&package=jdk&arch=x64&os=windows
        You may acquire a java 11 install here: https://adoptium.net/temurin/releases/?variant=openjdk11&version=11&package=jdk&arch=x64&os=windows
      1.18.2 and newer requires Java 17 (Java 18 will run better and work with 99% of mods, give it a try)
        You may acquire a Java 17 install here: https://adoptium.net/temurin/releases/?variant=openjdk17&version=17&package=jdk&arch=x64&os=windows
        You may acquire a Java 18 install here: https://adoptium.net/temurin/releases/?variant=openjdk18&version=18&package=jdk&arch=x64&os=windows
      1.20.5 and newer require Java 21
        You may acquire a Java 21 install here: https://adoptium.net/temurin/releases/?variant=openjdk21&version=21&package=jdk&arch=x64&os=windows

    .INPUTS

    None. You cannot pipe objects to start.ps1.

    .OUTPUTS

    None. start.ps1 does not generate any output to use in piping.
#>


Function PauseScript
{
    Write-Host "Press any key to continue" -ForegroundColor Yellow
    $host.ui.RawUI.ReadKey("NoEcho,IncludeKeyDown") > $null

    <#
    .SYNOPSIS

    Pause script execution. User input in the form of any keyboard key-press is required to continue execution.
#>
}

Function CrashServer
{
    param ($Message)
    Write-Host "${Message}"
    PauseScript
    exit 1

    <#
    .SYNOPSIS

    Crash script execution with exit code 1. Print $1 to the console.

    .PARAMETER Message
    The message to print to console before force-stopping script execution.
    #>
}

Function CommandAvailable
{
    param ($CmdName)
    return [bool](Get-Command -Name $CmdName -ErrorAction SilentlyContinue)

    <#
    .SYNOPSIS

    Check whether the command is available for execution. Can be used in if-statements.

    .PARAMETER CmdName
    The command to check for availability.
    #>
}

Function GetJavaVersion()
{
    $JavaFullversion = CMD /C "`"${Java}`" -fullversion 2>&1"
    $JavaFullversion = $JavaFullversion.Substring($JavaFullversion.IndexOf('"')+1).TrimEnd('"').Split('.')
    $script:JavaVersion = $JavaFullversion[0]

    if ([int]$JavaFullversion[0] -eq 1)
    {
        $script:JavaVersion = $JavaFullversion[1]
    }

    <#
    .SYNOPSIS

    Set $script:JavaVersion by checking $Java using -fullversion. Only the major version is stored, e.g. 8, 11, 17, 21.
    #>
}

Function InstallJava()
{
    Write-Host "No suitable Java installation was found on your system. Proceeding to Java installation."
    . .\install_java.ps1
    RunJavaInstallation
    if (!(CommandAvailable -cmdname "${Java}"))
    {
        CrashServer "Java installation failed. Couldn't find ${Java}."
    }

    <#
    .SYNOPSIS

    Sources the companion-script "install_java.ps1" and runs the contained function "Global:RunJavaInstallation" to install
    the required Java version for this modded Minecraft server.
    #>
}

Function DeleteFileSilently
{
    param ($FileToDelete)
    $ErrorActionPreference = "SilentlyContinue";
    if ((Get-Item "${FileToDelete}").PSIsContainer)
    {
        Remove-Item "${FileToDelete}" -Recurse
    }
    else
    {
        Remove-Item "${FileToDelete}"
    }
    $ErrorActionPreference = "Continue";

    <#
    .SYNOPSIS

    Quietly / silently delete the specified file from the filesystem. If a folder is specified, then the entire
    folder is deleted recursively.

    .PARAMETER FileToDelete
    The file or folder to delete silently, without printing messages or errors to console.
    #>
}

Function WriteFileUTF8NoBom
{
    param ($FilePath, $Content)
    $AbsolutePath = Join-Path -Path "$BaseDir" -ChildPath "$FilePath"
    New-Item $AbsolutePath -type file
    $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
    [IO.File]::WriteAllLines(($FilePath | Resolve-Path), $Content, $Utf8NoBomEncoding)

    <#
    .SYNOPSIS

    Write a text-file using UTF-8, but without a BOM. No-BOM UTF-8 files are required by the ServerStarterJar
    from the NeoForge-project for installing and running Forge and NeoForge servers, whilst using "user_jvm_args.txt".

    .PARAMETER FilePath
    The path to the file which should be writte. The file is created by this function, so no need to create it yourself.
    The path must be relative to the script. The function will take care of writing it in the base of the scripts
    working-directory.

    .PARAMETER Content
    The content to print to the file.
    #>
}

Function global:RunJavaCommand
{
    param ($CommandToRun)
    CMD /C "`"${Java}`" ${CommandToRun}"

    <#
    .SYNOPSIS

    Runs the passed string as a Java command with the Java installation set in $Java.

    .PARAMETER CommandToRun
    The command to run as a Java command.
    #>
}

Function DownloadIfNotExists
{
    param ($FileToCheck, $FileToDownload, $DownloadURL)
    if (!(Test-Path -Path $FileToCheck -PathType Leaf))
    {
        Write-Host "${FileToCheck} could not be found."
        Write-Host "Downloading ${FileToDownload}"
        Write-Host "from ${DownloadURL}"
        Invoke-WebRequest -URI "${DownloadURL}" -OutFile "${FileToDownload}"
        if (Test-Path -Path "${FileToDownload}" -PathType Leaf)
        {
            Write-Host "Download complete."
            return $true
        }
        else
        {
            return $false
        }
    }
    else
    {
        Write-Host "${FileToCheck} present."
        return $false
    }

    <#
    .SYNOPSIS

    Checks whether $FileToCheck exists. If not, then it is downloaded from $DownloadURL and stored as $FileToDownload.
    Can be used in if-statements.

    .PARAMETER FileToCheck
    The file to check for existence.

    .PARAMETER FileToDownload
    The filename to which store the download as.

    .PARAMETER DownloadURL
    The URL from which to download the file from.

    .OUTPUTS

    Boolean. $true if the file was downloaded and exists, $false otherwise.
    #>
}

Function global:RefreshServerJar
{
    if ("${ServerStarterJarForceFetch}" -eq "true")
    {
        DeleteFileSilently  'server.jar'
    }

    $ServerStarterJarDownloadURL = ""
    if ("${ServerStarterJarVersion}" -eq "latest")
    {
        $ServerStarterJarDownloadURL = "https://github.com/neoforged/ServerStarterJar/releases/latest/download/server.jar"
    }
    else
    {
        $ServerStarterJarDownloadURL = "https://github.com/neoforged/ServerStarterJar/releases/download/${ServerStarterJarVersion}/server.jar"
    }

    DownloadIfNotExists "server.jar" "server.jar" "${ServerStarterJarDownloadURL}"

    <#
    .SYNOPSIS

    Refresh the ServerStarterJar used for running Forge and NeoForge servers.
    Depending on the value of SERVERSTARTERJAR_FORCE_FETCH in the variables.txt the server.jar is force-refreshed.
    Meaning: If true, the server.jar will be deleted and then downloaded again.
    Depending on the value of SERVERSTARTERJAR_VERSION in the variables.txt a different version is fetched. More on
    this value in the variables.txt
    #>
}

Function global:CleanServerFiles
{
    $ErrorActionPreference = "SilentlyContinue";
    ForEach ($FileToRemove in $Cleanup)
    {
        $ToRemove = -join ($BaseDir.Trim('"'), "\" , ${FileToRemove}.Trim('"'));
        Remove-Item -Path "${ToRemove}" -Recurse -Force -Verbose -ErrorAction SilentlyContinue
    }
    $ErrorActionPreference = "Continue";

    <#
    .SYNOPSIS

    Clean up files created by installers or modloader servers, but leave server pack files untouched.
    Allows changing and re-installing the modloader, Minecraft and modloader versions.
    #>
}

Function global:SetupForge
{
    ""
    "Running Forge checks and setup..."
    $ForgeInstallerUrl = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/${MinecraftVersion}-${ModLoaderVersion}/forge-${MinecraftVersion}-${ModLoaderVersion}-installer.jar"
    $ForgeJarLocation = "do_not_manually_edit"
    if ([int]$Semantics[1] -le 16)
    {
        $ForgeJarLocation = "forge.jar"
        $script:LauncherJarLocation = "forge.jar"
        $script:ServerRunCommand = "${JavaArgs} -jar ${LauncherJarLocation} nogui"

        if ((DownloadIfNotExists "${ForgeJarLocation}" "forge-installer.jar" "${ForgeInstallerUrl}"))
        {
            "Forge Installer downloaded. Installing..."
            RunJavaCommand "-jar forge-installer.jar --installServer"

            "Renaming forge-${MinecraftVersion}-${ModLoaderVersion}.jar to forge.jar"
            Move-Item "forge-${MinecraftVersion}-${ModLoaderVersion}.jar" 'forge.jar'
            Move-Item "forge-${MinecraftVersion}-${ModLoaderVersion}-universal.jar" 'forge.jar'

            if ((Test-Path -Path "${ForgeJarLocation}" -PathType Leaf))
            {
                DeleteFileSilently  'forge-installer.jar'
                "Installation complete. forge-installer.jar deleted."
            }
            else
            {
                DeleteFileSilently  'forge-installer.jar'
                CrashServer "Something went wrong during the server installation. Please try again in a couple of minutes and check your internet connection."
            }
        }
    }
    else
    {
        if (${UseSSJ} -eq "false")
        {
            $ForgeJarLocation = "libraries/net/minecraftforge/forge/${MinecraftVersion}-${ModLoaderVersion}/forge-${MinecraftVersion}-${ModLoaderVersion}-server.jar"
            $script:ServerRunCommand = "@user_jvm_args.txt @libraries/net/minecraftforge/forge/${MinecraftVersion}-${ModLoaderVersion}/win_args.txt nogui"
            if ((DownloadIfNotExists "${ForgeJarLocation}" "forge-installer.jar" "${ForgeInstallerUrl}"))
            {
                "Forge Installer downloaded. Installing..."
                RunJavaCommand "-jar forge-installer.jar --installServer"
            }
        }
        else
        {
            $script:ServerRunCommand = "@user_jvm_args.txt ${SSJForgeArgs} -jar server.jar --installer-force --installer ${ForgeInstallerUrl} nogui"
            # Download ServerStarterJar to server.jar
            RefreshServerJar
        }

        Write-Host "Generating user_jvm_args.txt from variables..."
        Write-Host "Edit JAVA_ARGS in your variables.txt. Do not edit user_jvm_args.txt directly!"
        Write-Host "Manually made changes to user_jvm_args.txt will be lost in the nether!"
        DeleteFileSilently  'user_jvm_args.txt'
        $Content = "# Xmx and Xms set the maximum and minimum RAM usage, respectively.`n" +
                "# They can take any number, followed by an M or a G.`n" +
                "# M means Megabyte, G means Gigabyte.`n" +
                "# For example, to set the maximum to 3GB: -Xmx3G`n" +
                "# To set the minimum to 2.5GB: -Xms2500M`n" +
                "# A good default for a modded server is 4GB.`n" +
                "# Uncomment the next line to set it.`n" +
                "# -Xmx4G`n" +
                "${script:JavaArgs}"
        WriteFileUTF8NoBom "user_jvm_args.txt" $Content
    }

    <#
    .SYNOPSIS

    Download and install a Forge server for $ModLoaderVersion. For Minecraft 1.17 and newer the ServerStarterJar
    from the NeoForge-group is used. This has the benefit of making this server pack compatible with most hosting-companies.
    #>
}

# If modloader = NeoForge, run NeoForge-specific checks
Function global:SetupNeoForge
{
    ""
    "Running NeoForge checks and setup..."
    Write-Host "Generating user_jvm_args.txt from variables..."
    Write-Host "Edit JAVA_ARGS in your variables.txt. Do not edit user_jvm_args.txt directly!"
    Write-Host "Manually made changes to user_jvm_args.txt will be lost in the nether!"
    DeleteFileSilently  'user_jvm_args.txt'
    $Content = "# Xmx and Xms set the maximum and minimum RAM usage, respectively.`n" +
            "# They can take any number, followed by an M or a G.`n" +
            "# M means Megabyte, G means Gigabyte.`n" +
            "# For example, to set the maximum to 3GB: -Xmx3G`n" +
            "# To set the minimum to 2.5GB: -Xms2500M`n" +
            "# A good default for a modded server is 4GB.`n" +
            "# Uncomment the next line to set it.`n" +
            "# -Xmx4G`n" +
            "${script:JavaArgs}"
    WriteFileUTF8NoBom "user_jvm_args.txt" $Content

    if ([int]$Semantics[1] -eq 20 -And ($Semantics.count -eq 2 -Or [int]$Semantics[2] -eq 1))
    {
        $script:ServerRunCommand = "@user_jvm_args.txt -jar server.jar --installer-force --installer https://maven.neoforged.net/releases/net/neoforged/forge/${MinecraftVersion}-${ModLoaderVersion}/forge-${MinecraftVersion}-${ModLoaderVersion}-installer.jar nogui"
    }
    else
    {
        $script:ServerRunCommand = "@user_jvm_args.txt -jar server.jar --installer-force --installer ${ModLoaderVersion} nogui"
    }

    RefreshServerJar

    <#
    .SYNOPSIS

    Download and install a NeoForge server for $ModLoaderVersion. The ServerStarterJar from the NeoForge-group is used.
    This has the benefit of making this server pack compatible with most hosting-companies.
    #>
}

Function global:SetupFabric
{
    ""
    "Running Fabric checks and setup..."
    $FabricInstallerUrl = "https://maven.fabricmc.net/net/fabricmc/fabric-installer/${FabricInstallerVersion}/fabric-installer-${FabricInstallerVersion}.jar"
    $ImprovedFabricLauncherUrl = "https://meta.fabricmc.net/v2/versions/loader/${MinecraftVersion}/${ModLoaderVersion}/${FabricInstallerVersion}/server/jar"
    $ErrorActionPreference = "SilentlyContinue";
    $script:ImprovedFabricLauncherAvailable = [int][System.Net.WebRequest]::Create("${ImprovedFabricLauncherUrl}").GetResponse().StatusCode
    $ErrorActionPreference = "Continue";
    if ("${ImprovedFabricLauncherAvailable}" -eq "200")
    {
        "Improved Fabric Server Launcher available..."
        "The improved launcher will be used to run this Fabric server."
        $script:LauncherJarLocation = "fabric-server-launcher.jar"
        (DownloadIfNotExists "${script:LauncherJarLocation}" "${script:LauncherJarLocation}" "${ImprovedFabricLauncherUrl}") > $null
    }
    else
    {
        try
        {
            $ErrorActionPreference = "SilentlyContinue";
            $FabricAvailable = [int][System.Net.WebRequest]::Create("https://meta.fabricmc.net/v2/versions/loader/${MinecraftVersion}/${ModLoaderVersion}/server/json").GetResponse().StatusCode
            $ErrorActionPreference = "Continue";
        }
        catch
        {
            $FabricAvailable = "400"
        }
        if ("${FabricAvailable}" -ne "200")
        {
            CrashServer "Fabric is not available for Minecraft ${MinecraftVersion}, Fabric ${ModLoaderVersion}."
        }
        if ((DownloadIfNotExists "fabric-server-launch.jar" "fabric-installer.jar" "${FabricInstallerUrl}"))
        {
            "Installer downloaded..."
            $script:LauncherJarLocation = "fabric-server-launch.jar"
            RunJavaCommand "-jar fabric-installer.jar server -mcversion ${MinecraftVersion} -loader ${ModLoaderVersion} -downloadMinecraft"
            if ((Test-Path -Path 'fabric-server-launch.jar' -PathType Leaf))
            {
                DeleteFileSilently '.fabric-installer' -Recurse
                DeleteFileSilently 'fabric-installer.jar'
                "Installation complete. fabric-installer.jar deleted."
            }
            else
            {
                DeleteFileSilently  'fabric-installer.jar'
                CrashServer "fabric-server-launch.jar not found. Maybe the Fabric servers are having trouble. Please try again in a couple of minutes and check your internet connection."
            }
        }
        else
        {
            "fabric-server-launch.jar present. Moving on..."
            $script:LauncherJarLocation = "fabric-server-launch.jar"
        }
    }
    $script:ServerRunCommand = "${script:JavaArgs} -jar ${script:LauncherJarLocation} nogui"

    <#
    .SYNOPSIS

    Download and install a Fabric server for $ModLoaderVersion. If the Fabric Launcher is available for $MinecraftVersion
    and $ModLoaderVersion, it is downloaded and used, otherwise the regular Fabric-installer is downloaded and used.
    Checks are also performed to determine whether Fabric is available for $MinecraftVersion and $ModLoaderVersion.
    #>
}

Function global:SetupQuilt
{
    ""
    "Running Quilt checks and setup..."
    $QuiltInstallerUrl = "https://maven.quiltmc.org/repository/release/org/quiltmc/quilt-installer/${QuiltInstallerVersion}/quilt-installer-${QuiltInstallerVersion}.jar"
    if ((ConvertFrom-JSON (Invoke-WebRequest -Uri "https://meta.fabricmc.net/v2/versions/intermediary/${MinecraftVersion}")).Length -eq 0)
    {
        CrashServer "Quilt is not available for Minecraft ${MinecraftVersion}, Quilt ${ModLoaderVersion}."
    }
    elseif ((DownloadIfNotExists "quilt-server-launch.jar" "quilt-installer.jar" "${QuiltInstallerUrl}"))
    {
        "Installer downloaded. Installing..."
        RunJavaCommand "-jar quilt-installer.jar install server ${MinecraftVersion} --download-server --install-dir=."
        if ((Test-Path -Path 'quilt-server-launch.jar' -PathType Leaf))
        {
            DeleteFileSilently 'quilt-installer.jar'
            "Installation complete. quilt-installer.jar deleted."
        }
        else
        {
            DeleteFileSilently 'quilt-installer.jar'
            CrashServer "quilt-server-launch.jar not found. Maybe the Quilt servers are having trouble. Please try again in a couple of minutes and check your internet connection."
        }
    }
    $script:LauncherJarLocation = "quilt-server-launch.jar"
    $script:ServerRunCommand = "${JavaArgs} -jar ${LauncherJarLocation} nogui"

    <#
    .SYNOPSIS

    Download and install a Quilt server for $ModLoaderVersion.
    Checks are also performed to determine whether Quilt is available for $MinecraftVersion.
    #>
}

Function global:SetupLegacyFabric
{
    ""
    "Running LegacyFabric checks and setup..."
    $LegacyFabricInstallerUrl = "https://maven.legacyfabric.net/net/legacyfabric/fabric-installer/${LegacyFabricInstallerVersion}/fabric-installer-${LegacyFabricInstallerVersion}.jar"
    if ((ConvertFrom-JSON (Invoke-WebRequest -Uri "https://meta.legacyfabric.net/v2/versions/loader/${MinecraftVersion}")).Length -eq 0)
    {
        CrashServer "LegacyFabric is not available for Minecraft ${MinecraftVersion}, LegacyFabric ${ModLoaderVersion}."
    }
    elseif ((DownloadIfNotExists "fabric-server-launch.jar" "legacyfabric-installer.jar" "${LegacyFabricInstallerUrl}"))
    {
        "Installer downloaded. Installing..."
        RunJavaCommand "-jar legacyfabric-installer.jar server -mcversion ${MinecraftVersion} -loader ${ModLoaderVersion} -downloadMinecraft"
        if ((Test-Path -Path 'fabric-server-launch.jar' -PathType Leaf))
        {
            DeleteFileSilently 'legacyfabric-installer.jar'
            "Installation complete. legacyfabric-installer.jar deleted."
        }
        else
        {
            DeleteFileSilently 'legacyfabric-installer.jar'
            CrashServer "fabric-server-launch.jar not found. Maybe the LegacyFabric servers are having trouble. Please try again in a couple of minutes and check your internet connection."
        }
    }
    $script:LauncherJarLocation = "fabric-server-launch.jar"
    $script:ServerRunCommand = "${JavaArgs} -jar ${LauncherJarLocation} nogui"

    <#
    .SYNOPSIS

    Download and install a LegacyFabric server for $ModLoaderVersion.
    Checks are also performed to determine whether LegacyFabric is available for $MinecraftVersion.
    #>
}

Write-Host "Start script generated by ServerPackCreator SPC_SERVERPACKCREATOR_VERSION_SPC."
Write-Host "To change the launch settings of this server, such as JVM args / flags, Minecraft version, modloader version etc., edit the variables.txt-file."

# Ensures we are working in the directory which contains this script.
$BaseDir = Split-Path -parent $script:MyInvocation.MyCommand.Path
Push-Location $BaseDir

# Check whether the path to this directory contains spaces. Spaces in the path are prone to cause trouble.
if ( ${BaseDir}.Contains(" "))
{
    "WARNING! The current location of this script contains spaces. This may cause this server to crash!"
    "It is strongly recommended to move this server pack to a location whose path does NOT contain SPACES!"
    "Current path: ${BaseDir}"
    $WhyMustPowerShellBeThisWayLikeSeriouslyWhatTheFrag = Read-Host -Prompt 'Are you sure you want to continue? (Yes/No): '
    if (${WhyMustPowerShellBeThisWayLikeSeriouslyWhatTheFrag} -eq "Yes")
    {
        "Alrighty. Prepare for unforseen consequences, Mr. Freeman..."
    }
    else
    {
        CrashServer "User did not desire to run the server in a directory with spaces in its path."
    }
}

# It is not recommended to run the server using root as this introduces security risks to your system.
# Using your regular user is enough.
if ( (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
{
    Write-Host "Warning! Running with administrator-privileges is not recommended."
}

$ExternalVariablesFile = -join ("${BaseDir}", "\variables.txt");
if (!(Test-Path -Path $ExternalVariablesFile -PathType Leaf))
{
    CrashServer "ERROR! variables.txt not present. Without it the server can not be installed, configured or started."
}

$ExternalVariables = Get-Content -raw -LiteralPath $ExternalVariablesFile | ConvertFrom-StringData
$MinecraftVersion = $ExternalVariables['MINECRAFT_VERSION']
$ModLoader = $ExternalVariables['MODLOADER']
$ModLoaderVersion = $ExternalVariables['MODLOADER_VERSION']
$LegacyFabricInstallerVersion = $ExternalVariables['LEGACYFABRIC_INSTALLER_VERSION']
$FabricInstallerVersion = $ExternalVariables['FABRIC_INSTALLER_VERSION']
$QuiltInstallerVersion = $ExternalVariables['QUILT_INSTALLER_VERSION']
$Java = $ExternalVariables['JAVA']
$WaitForUserInput = $ExternalVariables['WAIT_FOR_USER_INPUT']
$JavaArgs = $ExternalVariables['JAVA_ARGS']
$AdditionalArgs = $ExternalVariables['ADDITIONAL_ARGS']
$SSJForgeArgs = $ExternalVariables['SSJ_FORGE_ARGS']
$Restart = $ExternalVariables['RESTART']
$SkipJavaCheck = $ExternalVariables['SKIP_JAVA_CHECK']
$RecommendedJavaVersion = $ExternalVariables['RECOMMENDED_JAVA_VERSION']
$ServerStarterJarForceFetch = $ExternalVariables['SERVERSTARTERJAR_FORCE_FETCH']
$ServerStarterJarVersion = $ExternalVariables['SERVERSTARTERJAR_VERSION']
$UseSSJ = $ExternalVariables['USE_SSJ']
$Cleanup = $ExternalVariables['CLEANUP'].Split(",")
$LauncherJarLocation = "do_not_manually_edit"
$ServerRunCommand = "do_not_manually_edit"
$JavaVersion = "do_not_manually_edit"
$Semantics = ${MinecraftVersion}.Split(".")

# Clears the "" from the beginning and end of the Java, JavaArgs, AdditionalArgs, SSJArgs vars
$Java = $Java.Trim('"')
$JavaArgs = $JavaArgs.Trim('"')
$AdditionalArgs = $AdditionalArgs.Trim('"')
$SSJForgeArgs = $SSJForgeArgs.Trim('"')

# If Java checks are desired, then the available Java version is compared to the one required by the Minecraft server.
# Should no Java be found, or an incorrect version be available, the required one is installed by running installJava.
if ("${SkipJavaCheck}" -eq "true")
{
    "Skipping Java version check."
}
else
{
    if ("${Java}" -eq "java")
    {
        if (!(CommandAvailable -cmdname "${Java}"))
        {
            InstallJava
        }
        else
        {
            GetJavaVersion
            if ($script:JavaVersion -match '[0-9]+')
            {
                if ($script:JavaVersion -ne $RecommendedJavaVersion)
                {
                    InstallJava
                }
            }
            else
            {
                InstallJava
            }
        }
    }
    else
    {
        GetJavaVersion
        Write-Host "Detected $($Semantics[0]).$($Semantics[1]).$($Semantics[2]) - Java $($JavaVersion)"
        if ($script:JavaVersion -ne $RecommendedJavaVersion)
        {
            $script:Java = "java"
            InstallJava
        }
    }
}

# Check and warn the user if a 32bit Java-installation is used. Realistically, this should happen less and less, but
# it does happen from time to time. Best to warn people about it.
$Bit = CMD /C "`"${Java}`" -version 2>&1"
if (( ${Bit} | Select-String "32-Bit").Length -gt 0)
{
    Write-Host "WARNING! 32-Bit Java detected! It is highly recommended to use a 64-Bit version of Java!"
}

$ReInstall = $args
$PreviousRunFile = -join ("${BaseDir}", "\.previousrun");

if ($ReInstall -eq '--cleanup')
{
    Write-Host "Running cleanup..."
    CleanServerFiles
}
elseif (Test-Path -Path $PreviousRunFile -PathType Leaf)
{
    $PreviousRunValues = Get-Content -raw -LiteralPath $PreviousRunFile | ConvertFrom-StringData
    $PreviousMinecraftVersion = $PreviousRunValues['PREVIOUS_MINECRAFT_VERSION']
    $PreviousModLoader = $PreviousRunValues['PREVIOUS_MODLOADER']
    $PreviousModLoaderVersion = $PreviousRunValues['PREVIOUS_MODLOADER_VERSION']
    if (!("${PreviousMinecraftVersion}" -eq "${MinecraftVersion}") -or
        !("${PreviousModLoader}" -eq "${ModLoader}") -or
        !("${PreviousModLoaderVersion}" -eq "${ModLoaderVersion}"))
    {
        Write-Host "Minecraft version, modloader or modloader version have changed. Cleaning up..."
        CleanServerFiles
    }
}

"PREVIOUS_MINECRAFT_VERSION=${MinecraftVersion}`n" +
"PREVIOUS_MODLOADER=${ModLoader}`n" +
"PREVIOUS_MODLOADER_VERSION=${ModLoaderVersion}" | Out-File $PreviousRunFile -encoding utf8

switch (${ModLoader})
{
    NeoForge
    {
        SetupNeoForge
    }
    Forge
    {
        SetupForge
    }
    Fabric
    {
        SetupFabric
    }
    Quilt
    {
        SetupQuilt
    }
    LegacyFabric
    {
        SetupLegacyFabric
    }
    default
    {
        CrashServer "Incorrect modloader specified: ${ModLoader}"
    }
}

if (!(Test-Path -Path 'eula.txt' -PathType Leaf))
{
    "Mojang's EULA has not yet been accepted. In order to run a Minecraft server, you must accept Mojang's EULA."
    "Mojang's EULA is available to read at https://aka.ms/MinecraftEULA"
    "If you agree to Mojang's EULA then type 'I agree'"
    $Answer = Read-Host -Prompt 'Answer'
    if (${Answer} -eq "I agree")
    {
        "User agreed to Mojang's EULA."
        "#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://aka.ms/MinecraftEULA).`n" +
                "eula=true" | Out-File eula.txt -encoding utf8
    }
    else
    {
        CrashServer "User did not agree to Mojang's EULA. Entered: ${Answer}. You can not run a Minecraft server unless you agree to Mojang's EULA."
    }
}

""
"Starting server..."
"Minecraft version:              ${MinecraftVersion}"
"Modloader:                      ${ModLoader}"
"Modloader version:              ${ModLoaderVersion}"
"LegacyFabric Installer Version: ${LegacyFabricInstallerVersion}"
"Fabric Installer Version:       ${FabricInstallerVersion}"
"Quilt Installer Version:        ${QuiltInstallerVersion}"
"Java Args:                      ${JavaArgs}"
"Additional Args:                ${AdditionalArgs}"
"SSJ Forge Args:                 ${SSJForgeArgs}"
"Java Path:                      ${Java}"
"Wait For User Input:            ${WaitForUserInput}"
if (!("${LauncherJarLocation}" -eq "do_not_manually_edit"))
{
    "Launcher JAR:                   ${LauncherJarLocation}"
}
"Run Command:       ${Java} ${AdditionalArgs} ${ServerRunCommand}"
"Java version:"
RunJavaCommand "-version"
""

# Depending on $Restart the server runs in a loop, to make sure it comes right back up after crashing. Force exit can be
# achieved by hitting CTRL+C multiple times. Variables are not reloaded between server runs. Quit the script and re-run
# it if you wish to reload the variables.
while ($true)
{
    RunJavaCommand "${AdditionalArgs} ${ServerRunCommand}"
    if ("${SkipJavaCheck}" -eq "true")
    {
        "Java version check was skipped. Did the server stop or crash because of a Java version mismatch?"
        "Detected $($Semantics[0]).$($Semantics[1]).$($Semantics[2]) - Java $($JavaVersion), recommended $($RecommendedJavaVersion)"
    }
    if (!("${Restart}" -eq "true"))
    {
        Write-Host "Exiting..."
        if ("${WaitForUserInput}" -eq "true")
        {
            PauseScript
        }
        exit 0
    }
    "Automatically restarting server in 5 seconds. Press CTRL + C to abort and exit."
    Start-Sleep -Seconds 5
}

""